package com.nullpointer.xchatfinal.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smackx.muc.DiscussionHistory;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.media.RingtoneManager;
import android.net.ConnectivityManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.provider.Settings;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.LocalBroadcastManager;
import android.telephony.TelephonyManager;
import android.widget.ImageView;

import com.androidquery.AQuery;
import com.androidquery.callback.AjaxStatus;
import com.androidquery.callback.BitmapAjaxCallback;
import com.nullpointer.xchatfinal.App;
import com.nullpointer.xchatfinal.MainActivity;
import com.nullpointer.xchatfinal.R;
import com.nullpointer.xchatfinal.database.MessagesDataManager;
import com.nullpointer.xchatfinal.object.ChatType;
import com.nullpointer.xchatfinal.object.DetailedMessage;
import com.nullpointer.xchatfinal.object.GroupChat;
import com.nullpointer.xchatfinal.object.GroupMessageContent;
import com.nullpointer.xchatfinal.object.HistoryMessage;
import com.nullpointer.xchatfinal.object.MessageContent;
import com.nullpointer.xchatfinal.object.TargetChat;
import com.nullpointer.xchatfinal.object.MessageContent.MsgType;
import com.nullpointer.xchatfinal.object.OnJoinRoomListener;
import com.nullpointer.xchatfinal.object.OnSendGroupMessageListener;
import com.nullpointer.xchatfinal.object.OnSendPrivateMessageListener;
import com.nullpointer.xchatfinal.object.PrivateMessageContent;
import com.nullpointer.xchatfinal.object.User;
import com.nullpointer.xchatfinal.object.UserInfo;
import com.nullpointer.xchatfinal.service.NetworkReceiver.OnNetworkListener;
import com.nullpointer.xchatfinal.utils.Common;
import com.nullpointer.xchatfinal.utils.DataManager;
import com.nullpointer.xchatfinal.utils.Emoticons;
import com.nullpointer.xchatfinal.utils.Error.ErrorCode;
import com.nullpointer.xchatfinal.utils.KiraGameLog;
import com.nullpointer.xchatfinal.utils.Utils;
import com.quickblox.auth.QBAuth;
import com.quickblox.auth.model.QBSession;
import com.quickblox.chat.QBChatService;
import com.quickblox.chat.QBGroupChat;
import com.quickblox.chat.QBGroupChatManager;
import com.quickblox.chat.QBPrivateChat;
import com.quickblox.chat.exception.QBChatException;
import com.quickblox.chat.listeners.QBGroupChatManagerListener;
import com.quickblox.chat.listeners.QBMessageListener;
import com.quickblox.chat.listeners.QBParticipantListener;
import com.quickblox.chat.listeners.QBPrivateChatManagerListener;
import com.quickblox.chat.model.QBChatMessage;
import com.quickblox.chat.model.QBDialog;
import com.quickblox.chat.model.QBPresence;
import com.quickblox.core.QBEntityCallbackImpl;
import com.quickblox.core.QBSettings;
import com.quickblox.core.helper.StringifyArrayList;
import com.quickblox.core.request.QBRequestGetBuilder;
import com.quickblox.core.server.BaseService;
import com.quickblox.users.QBUsers;
import com.quickblox.users.model.QBUser;
import com.quickblox.videochat.core.QBVideoChatController;

public class ChatService extends Service {

	// UI Name
	public static final String UI_NAME_MAIN = "UI_NAME_MAIN";
	public static final String UI_NAME_CHAT_PRIVATE = "UI_NAME_CHAT_PRIVATE";
	public static final String UI_NAME_SPLASH = "UI_NAME_SPLASH";

	// BROADCAST NAME
	public static final String BROADCAST_NAME_MAIN = "com.kiragame.xchat.changeuimain";
	public static final String BROADCAST_NAME_CHAT_PRIVATE = "com.kiragame.xchat.changeuichatprivate";
	public static final String BROADCAST_NAME_SPLASH = "com.kiragame.xchat.changeuisplash";

	// COMMON CONTROL
	public static final String CONTROL_MESSAGE = "CONTROL_MESSAGE";
	public static final String CONTROL_ACTION = "CONTROL_ACTION";

	// ACTION UI MAIN
	public static final String MAIN_ACTION_NOTIFY_CONNECTION_ONLINE = "MAIN_ACTION_NOTIFY_CONNECTION_ONLINE";
	public static final String MAIN_ACTION_NOTIFY_CONNECTION_OFFLINE = "MAIN_ACTION_NOTIFY_CONNECTION_OFFLINE";

	public static final String MAIN_ACTION_ON_PRE_UPDATE_PROFILE = "MAIN_ACTION_ON_PRE_UPDATE_PROFILE";
	public static final String MAIN_ACTION_ON_FINISHED_UPDATE_PROFILE = "MAIN_ACTION_ON_FINISHED_UPDATE_PROFILE";

	public static final String MAIN_ACTION_ON_USER_ENTERED_ROOM = "MAIN_ACTION_ON_USER_ENTERED_ROOM";
	public static final String MAIN_ACTION_ON_USER_LEFT_ROOM = "MAIN_ACTION_ON_USER_LEFT_ROOM";

	public static final String MAIN_ACTION_ON_PRE_LOG_OUT = "MAIN_ACTION_ON_PRE_LOG_OUT";
	public static final String MAIN_ACTION_ON_FINISHED_LOG_OUT = "MAIN_ACTION_ON_FINISHED_LOG_OUT";

	public static final String MAIN_ACTION_ON_RECEIVED_MESSAGE_GROUP = "MAIN_ACTION_ON_RECEIVED_MESSAGE_GROUP";
	public static final String MAIN_ACTION_ON_RECEIVED_MESSAGE_PRIVATE = "MAIN_ACTION_ON_RECEIVED_MESSAGE_PRIVATE";

	public static final String SPLASH_ACTION_ON_PRE_SETUP_SERVER = "SPLASH_ACTION_ON_PRE_SETUP_SERVER";
	public static final String SPLASH_ACTION_ON_FINISHED_SETUP_SERVER = "SPLASH_ACTION_ON_FINISHED_SETUP_SERVER";
	public static final String SPLASH_ACTION_NOTIFY_CONNECTION_ONLINE = "SPLASH_ACTION_NOTIFY_CONNECTION_ONLINE";
	public static final String SPLASH_ACTION_NOTIFY_CONNECTION_OFFLINE = "SPLASH_ACTION_NOTIFY_CONNECTION_OFFLINE";

	public static final String MAIN_ACTION_ON_PRE_SETUP_SERVER = "MAIN_ACTION_ON_PRE_SETUP_SEVER";
	public static final String MAIN_ACTION_ON_FINISHED_SETUP_SERVER = "MAIN_ACTION_ON_FINISHED_SETUP_SERVER";

	// ACTION UI CHAT PRIVATE
	private static ChatService mChatService;
	private static boolean isSetupDone = true;
	// private Context mContext;

	// QuickBlox
	// KEY
	private static final String APP_ID = "17401";
	private static final String AUTH_KEY = "9hRTDs9WePY9msJ";
	private static final String AUTH_SECRET = "HpFVfrbOkeYtKT5";

	// private static final String APP_ID = "17932";
	// private static final String AUTH_KEY = "jpfSBwr5JsmZ6Vd";
	// private static final String AUTH_SECRET = "FR4OPqTHArJUP3Y";

	private static QBUser currentQBUser = null;
	private final String PASSWORD_DEFAULT = "abcd123456";
	private final String USER_EXIST = "login has already been taken";
	private final String NOT_REGISTER = "Unauthorized";
	private final String HAVE_LOGGED = "already logged";
	private final String NO_RESPONSE = "NoResponseException";
	private final String NOT_GROUP_MEMBER = "registration-required";
	private final String SESSION_NOT_EXIST = "session does not exist";
	private LocalBroadcastManager broadcaster;
	public static QBPrivateChat chat = null;

	// private String name;
	// private String status;

	private NetworkReceiver networkReceiver;
	private NotificationManager notificationManager;
	private NotificationCompat.Builder builder;
	private Bitmap stub;

	// AUTO CHAT
	private final String AUTO_CHAT_KEY = "a1ffed7f-fa53-4488-83d8-e7cd9ea8448c";
	private MessageContent autoMessageContent;

	public static final String ONLINE = "online";
	public static final String OFFLINE = "offline";
	public static final String INVISIBLE = "invisible";
	public static final String VISIBLE = "visible";
	public static final String NORMAL_USER = "user";
	public static final String BLOCKED_USER = "blocked";
	private static final String APP_NAME = "Xchat";

	public static ChatService getInstance() {
		return mChatService;
	}

	private void setUpChatService() {
		KiraGameLog.d("setUpChatService");
		notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
		builder = new NotificationCompat.Builder(this);
		AQuery aq = new AQuery(this);
		stub = aq.getCachedImage(R.drawable.ic_no_avatar);
		generateDeviceID();
		broadcaster = LocalBroadcastManager.getInstance(this);
	}

	@Override
	public void onCreate() {
		KiraGameLog.d("----------- ChatService onCreate() ------------");
		super.onCreate();
		if (DataManager.getInstance(this).isLogout()) {
			stopSelf();
			return;
		}
		mChatService = this;
		setUpChatService();
		setUpServer();
		networkReceiver = new NetworkReceiver();
		networkReceiver.setOnNetworkListener(new OnNetworkListener() {

			@Override
			public void onOnline() {
				if (!isLogin()) {
					KiraGameLog.d("NetworkReceiver onOnline resetup sever!");
					setUpServer();
				}
				notifyConnectionOnline();
			}

			@Override
			public void onOffine() {
				notifyConnectionOffline();
			}
		});
		this.registerReceiver(networkReceiver, new IntentFilter(
				ConnectivityManager.CONNECTIVITY_ACTION));

	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		return START_STICKY;
	}

	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}

	private void generateDeviceID() {
		if (DataManager.getInstance(this).getDeviceID() == null) {
			String deviceId;
			final TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
			if (mTelephony.getDeviceId() != null) {
				deviceId = mTelephony.getDeviceId(); // *** use for mobiles
			} else {
				deviceId = Settings.Secure.getString(getContentResolver(),
						Settings.Secure.ANDROID_ID); // *** use for tablets
			}

			// String android_id = Secure.getString(this.getContentResolver(),
			// Secure.ANDROID_ID);
			DataManager.getInstance(this).setDeviceID(deviceId + APP_NAME);
		}
	}

	public void setUpServer() {
		if (!isSetupDone) {
			KiraGameLog.d("Server is setup..., cant resetup server!");
			return;
		}

		callBackOnPreSetupServer();
		KiraGameLog.d("Checking quickblox setup...");

		QBChatService.setDebugEnabled(false);

		QBSettings.getInstance().fastConfigInit(APP_ID, AUTH_KEY, AUTH_SECRET);
		if (!QBChatService.isInitialized()) {
			QBChatService.init(this);
		}

		// create QB user
		//
		final QBUser qbUser = new QBUser(DataManager.getInstance(this)
				.getDeviceID(), PASSWORD_DEFAULT);

		QBAuth.createSession(new QBEntityCallbackImpl<QBSession>() {
			@Override
			public void onSuccess(QBSession session, Bundle params) {
				KiraGameLog.d("QBAuth.createSession success!");
				QBUsers.signIn(qbUser, new QBEntityCallbackImpl<QBUser>() {
					@Override
					public void onSuccess(QBUser qbUser, Bundle args) {
						KiraGameLog.d("QBUsers.signIn success!");
						User localUser = DataManager.getInstance(
								ChatService.this).getCurrentUser();
						User serverUser = Utils.convertQBUserToUser(qbUser);
						if (localUser != null) {
							if (localUser.getName() != null) {
								serverUser.setName(localUser.getName());
							}
							UserInfo localInfo = localUser.getUserInfo();
							UserInfo serverInfo = serverUser.getUserInfo();
							if (localInfo.getFaceBookID() != null) {
								serverInfo.setFaceBookID(localInfo
										.getFaceBookID());
							}

							if (localInfo.getAvatarID() != null) {
								serverInfo.setAvatarID(localInfo.getAvatarID());
							}

							if (localInfo.getBirthday() != 0) {
								serverInfo.setBirthday(localInfo.getBirthday());
							}

							// update gender from local
							serverInfo.setGender(localInfo.getGender());
							serverUser.setUserInfo(serverInfo);
						}
						DataManager.getInstance(ChatService.this)
								.setCurrentUser(serverUser);

						qbUser.setPassword(PASSWORD_DEFAULT);
						qbUser.setOldPassword(PASSWORD_DEFAULT);

						App app = (App) getApplication();
						app.setCurrentUser(qbUser);
						currentQBUser = qbUser;
						loginToChatService(qbUser);
					}

					@Override
					public void onError(List<String> errors) {
						KiraGameLog.d("QBUsers.signIn error! " + errors);
						if (errors.get(0).equals(NOT_REGISTER)) {
							KiraGameLog.d("User not exist... Register now! ");
							registerQuickblox();
						} else {
							callBackOnFinishedSetupServer("SignIn Error: "
									+ errors.toString());
						}
					}
				});
			}

			@Override
			public void onError(List<String> errors) {
				KiraGameLog.e("QBAuth.createSession fail! " + errors);
				callBackOnFinishedSetupServer("Create Session Error: "
						+ errors.toString());
			}
		});
	}

	private void loginToChatService(QBUser qbUser) {
		KiraGameLog.d("loginToChatService...");
		QBChatService.getInstance().login(qbUser, new QBEntityCallbackImpl() {
			@Override
			public void onSuccess() {
				KiraGameLog.d("qbChatService.login success!");
				setUpChatConnection();
			}

			@Override
			public void onError(List errors) {
				if (((String) errors.get(0)).contains(HAVE_LOGGED)) {
					setUpChatConnection();
				} else {
					KiraGameLog.e("qbChatService.login error " + errors);
					callBackOnFinishedSetupServer("Login chat service Error: "
							+ errors.toString());
				}

			}
		});
	}

	protected void registerQuickblox() {
		QBUser qbUser = new QBUser(DataManager.getInstance(this).getDeviceID(),
				PASSWORD_DEFAULT);
		final User currentUser = DataManager.getInstance(ChatService.this)
				.getCurrentUser();
		UserInfo userInfo = currentUser.getUserInfo();
		qbUser.setCustomData(userInfo.toJson());
		qbUser.setFullName(qbUser.getFullName());
		qbUser.setOldPassword(PASSWORD_DEFAULT);

		StringifyArrayList<String> tags = new StringifyArrayList<String>();
		tags.add(UserInfo.genderText[userInfo.getGender().ordinal()]);
		tags.add(NORMAL_USER);
		tags.add(OFFLINE);
		tags.add(VISIBLE);
		qbUser.setTags(tags);
		// qbUser.setFacebookId(userInfo.getFaceBookID());

		QBUsers.signUpSignInTask(qbUser, new QBEntityCallbackImpl<QBUser>() {
			@Override
			public void onSuccess(final QBUser mQBUser, Bundle bundle) {
				KiraGameLog.d("SignUpSignInTask success! " + mQBUser);
				currentUser.setUserID(mQBUser.getId());
				DataManager.getInstance(ChatService.this).setCurrentUser(
						currentUser);
				mQBUser.setPassword(PASSWORD_DEFAULT);
				mQBUser.setOldPassword(PASSWORD_DEFAULT);
				App app = (App) getApplication();
				app.setCurrentUser(mQBUser);
				currentQBUser = mQBUser;
				loginToChatService(mQBUser);
			}

			@Override
			public void onError(List<String> strings) {
				KiraGameLog.e("SignUpSignInTask fail " + strings);
				callBackOnFinishedSetupServer("Sign In Error: "
						+ strings.toString());
			}
		});

	}

	ConnectionListener connectionListener = new ConnectionListener() {

		@Override
		public void authenticated(XMPPConnection arg0) {
			KiraGameLog.d("connectionListener authenticated");
		}

		@Override
		public void connected(XMPPConnection arg0) {
			KiraGameLog.d("connectionListener connected");
		}

		@Override
		public void connectionClosed() {
			KiraGameLog.d("connectionListener connectionClosed");
		}

		@Override
		public void connectionClosedOnError(Exception arg0) {
			KiraGameLog.d("connectionListener connectionClosedOnError");
		}

		@Override
		public void reconnectingIn(int arg0) {
			KiraGameLog.d("connectionListener reconnectingIn");
		}

		@Override
		public void reconnectionFailed(Exception arg0) {
			KiraGameLog.d("connectionListener reconnectionFailed");
		}

		@Override
		public void reconnectionSuccessful() {
			KiraGameLog.d("connectionListener reconnectionSuccessful");
		}
	};

	private void setUpChatConnection() {
		// Start sending presences
		try {
			QBChatService.getInstance().startAutoSendPresence(30);
		} catch (SmackException.NotLoggedInException e) {
			e.printStackTrace();
		}
		// clear before add new
		QBChatService.getInstance().addConnectionListener(connectionListener);
		setUpChatPrivate();
		setUpChatGroup();
		setUpVideoCall();
		rejoinAllJoinedGroup();
		updateOnlineStatus();
		callBackOnFinishedSetupServer(null);
	}

	private void updateOnlineStatus() {
		Handler handler = new Handler(Looper.getMainLooper());
		handler.post(new Runnable() {

			@Override
			public void run() {
				boolean isInvisible = DataManager.getInstance(ChatService.this)
						.getInvisibleSetting();
				ChatService.getInstance().updateProfile(true, isInvisible);
			}
		});

	}

	// VIDEO CHAT
	private void setUpVideoCall() {
		try {
			QBVideoChatController.getInstance()
					.initQBVideoChatMessageListener();
		} catch (XMPPException e) {
			e.printStackTrace();
		}
	}

	// CHAT PRAVITE
	private void setUpChatPrivate() {
		QBChatService.getInstance().getPrivateChatManager()
				.addPrivateChatManagerListener(privateChatManagerListener);
	}

	QBPrivateChatManagerListener privateChatManagerListener = new QBPrivateChatManagerListener() {
		@Override
		public void chatCreated(final QBPrivateChat privateChat,
				final boolean createdLocally) {
			if (!createdLocally) {
				privateChat.addMessageListener(privateChatMessageListener);
			}
		}
	};

	QBMessageListener<QBPrivateChat> privateChatMessageListener = new QBMessageListener<QBPrivateChat>() {

		@Override
		public void processMessageRead(QBPrivateChat arg0, String arg1) {
			// TODO Auto-generated method stub

		}

		@Override
		public void processMessageDelivered(QBPrivateChat arg0, String arg1) {
			// TODO Auto-generated method stub

		}

		@Override
		public void processMessage(QBPrivateChat qbPrivateChat,
				QBChatMessage chatMessage) {
			String content = chatMessage.getBody();
			MessageContent messageContent = MessageContent
					.fromJsonToMessageContent(content);
			if (messageContent == null) {
				return;
			}
			Integer fromUserID = messageContent.getFromUserID();
			String sendFrom = String.valueOf(fromUserID);
			Integer currentUserId = DataManager.getInstance(ChatService.this)
					.getCurrentUser().getUserID();
			if (DataManager.getInstance(ChatService.this).getBlockUsers()
					.contains(sendFrom)
					|| currentUserId.equals(fromUserID)) {
				// BlockUser
				return;
			}

			if (messageContent.getMsgType() == MsgType.IMAGE
					|| messageContent.getMsgType() == MsgType.TEXT) {
				// || messageContent.getMsgType() == MsgType.ACCEPT_VIDEO_CALL
				// || messageContent.getMsgType() == MsgType.REJECT_VIDEO_CALL
				// || messageContent.getMsgType() ==
				// MsgType.SEND_CHAT_VIDEO_REQUEST) {
				// if (messageContent.getMsgType() ==
				// MsgType.SEND_CHAT_VIDEO_REQUEST) {
				// messageContent
				// .setMsgType(MsgType.RECEIVE_CHAT_VIDEO_REQUEST);
				// String text = messageContent.getName()
				// + " video calling...";
				// messageContent.setContent(text);
				// }
				sendDelivered(messageContent);
				TargetChat currentTargetChat = DataManager.getInstance(
						ChatService.this).getCurrentTargetChat();
				// add to database
				DetailedMessage detailedMessage = new DetailedMessage();
				HistoryMessage historyMessage = new HistoryMessage();
				detailedMessage.setTargetID(sendFrom);
				detailedMessage.setTargetName(messageContent.getName());
				detailedMessage.setMessageContent(messageContent);
				historyMessage.setDetailedMessage(detailedMessage);
				if (currentTargetChat != null) {
					String currentChatID = currentTargetChat.getFromID();
					if (!currentChatID.equals(sendFrom)) {
						historyMessage.setUnreadNum(1);
					} else {
						historyMessage.setUnreadNum(0);
					}
				} else {
					historyMessage.setUnreadNum(1);
				}
				historyMessage.setChatType(ChatType.CHAT_PRIVATE);
				MessagesDataManager.getInstance(ChatService.this)
						.saveMessageDatabase(historyMessage);
				boolean isAppPause = DataManager.getInstance(ChatService.this)
						.isAppPause();
				if (isAppPause) {
					showNotify(messageContent);
				} else {

					if (currentTargetChat != null) {
						String currentChatID = currentTargetChat.getFromID();
						if (!currentChatID.equals(sendFrom)) {
							showNotify(messageContent);
						}
					} else {
						showNotify(messageContent);
					}
				}

			}
			onReceivedMessagePrivate(content);
		}

		@Override
		public void processError(QBPrivateChat arg0, QBChatException arg1,
				QBChatMessage arg2) {
			// TODO Auto-generated method stub

		}
	};

	public void sendMessagePrivate(PrivateMessageContent privateMessageContent,
			OnSendPrivateMessageListener listener) {
		if (listener != null) {
			listener.onPreSendMessage(privateMessageContent);
		}
		if (!isLogin()
				|| QBChatService.getInstance().getPrivateChatManager() == null) {
			if (listener != null) {
				listener.onSendMessageFailed(ErrorCode.CONNECT_SERVER_ERROR,
						privateMessageContent);
			}
			KiraGameLog.d("sendMessagePrivate error : not login");
			return;
		}
		Integer toUserID = Integer.parseInt(privateMessageContent
				.getTargetUserID());
		try {
			QBPrivateChat privateChat = QBChatService.getInstance()
					.getPrivateChatManager().getChat(toUserID);
			if (privateChat == null) {
				privateChat = QBChatService.getInstance()
						.getPrivateChatManager()
						.createChat(toUserID, privateChatMessageListener);
			}
			MessageContent msg = privateMessageContent.getMessageContent();
			privateChat.sendMessage(msg.toJson());

			if (listener != null) {
				listener.onSendMessageSuccessed(privateMessageContent);
			}
			KiraGameLog.d("sendMessagePrivate success "
					+ privateMessageContent.toJson());

		} catch (XMPPException e) {
			KiraGameLog.d("sendMessagePrivate error ");
			e.printStackTrace();

			if (listener != null) {
				listener.onSendMessageFailed(ErrorCode.CAN_NOT_SEND_MESSAGE,
						privateMessageContent);
			}
		} catch (SmackException.NotConnectedException e) {
			KiraGameLog.d("sendMessagePrivate error ");
			e.printStackTrace();
			if (listener != null) {
				listener.onSendMessageFailed(ErrorCode.CAN_NOT_SEND_MESSAGE,
						privateMessageContent);
			}
		}
	}

	protected void sendDelivered(MessageContent messageContent) {
		PrivateMessageContent privateMessageContent = new PrivateMessageContent(
				this, messageContent.toJson(), MsgType.MESSAGE_STATE_DILIVER,
				Integer.toString(messageContent.getFromUserID()));
		sendMessagePrivate(privateMessageContent, null);
	}

	public void leaveChat() {
		try {
			QBChatService
					.getInstance()
					.getPrivateChatManager()
					.removePrivateChatManagerListener(
							privateChatManagerListener);
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	// CHAT ROOM

	private void setUpChatGroup() {
		QBChatService.getInstance().getGroupChatManager()
				.addGroupChatManagerListener(qbGroupChatManagerListener);
	}

	QBGroupChatManagerListener qbGroupChatManagerListener = new QBGroupChatManagerListener() {

		@Override
		public void chatCreated(QBGroupChat qbGroupChat) {
			KiraGameLog.d("qbGroupChatManagerListener chatCreated "
					+ qbGroupChat);
			qbGroupChat.addMessageListener(groupChatQBMessageListener);
			qbGroupChat.addParticipantListener(participantListener);
		}
	};

	private QBParticipantListener participantListener = new QBParticipantListener() {

		@Override
		public void processPresence(QBGroupChat arg0, QBPresence arg1) {
			// TODO Auto-generated method stub

		}
	};

	public void sendMessageChatGroup(GroupMessageContent groupMessageContent,
			OnSendGroupMessageListener listener) {
		if (listener != null) {
			listener.onPreSendMessage();
		}
		if (!isLogin()) {
			if (listener != null) {
				listener.onSendMessageFailed(ErrorCode.CONNECT_SERVER_ERROR,
						groupMessageContent);
			}
			KiraGameLog.d("sendMessageChatGroup error : not login");
			return;
		}
		QBGroupChatManager groupChatManager = QBChatService.getInstance()
				.getGroupChatManager();
		if (groupChatManager == null) {
			if (listener != null) {
				listener.onSendMessageFailed(ErrorCode.CONNECT_SERVER_ERROR,
						groupMessageContent);
			}
			KiraGameLog.d("sendMessageChatGroup error : groupChatManager null");
			return;
		}

		String roomJid = groupMessageContent.getFromGroupJID();

		QBGroupChat currentChatRoom = groupChatManager.getGroupChat(roomJid);
		if (currentChatRoom == null) {
			if (listener != null) {
				listener.onSendMessageFailed(ErrorCode.JOIN_ROOM_FAIL,
						groupMessageContent);
			}
			KiraGameLog.d("sendMessageChatGroup error : currentChatRoom null");
			return;
		}

		try {
			QBChatMessage chatMessage = new QBChatMessage();
			chatMessage.setBody(groupMessageContent.toJson());
			chatMessage.setProperty("save_to_history", "1");
			currentChatRoom.sendMessage(chatMessage);
			if (listener != null) {
				listener.onSendMessageSuccessed(groupMessageContent
						.getMessageContent());
			}
			KiraGameLog.d("sendMessageChatGroup success");
		} catch (XMPPException e) {
			KiraGameLog.d("send message to chat room fail: " + e.getMessage());
			e.printStackTrace();
			if (listener != null) {
				listener.onSendMessageFailed(ErrorCode.CAN_NOT_SEND_MESSAGE,
						groupMessageContent);
			}
		} catch (SmackException.NotConnectedException e) {
			KiraGameLog.d("send message to chat room fail: " + e.getMessage());
			e.printStackTrace();
			if (listener != null) {
				listener.onSendMessageFailed(ErrorCode.CAN_NOT_SEND_MESSAGE,
						groupMessageContent);
			}
		} catch (IllegalStateException e) {
			KiraGameLog.d("send message to chat room fail: " + e.getMessage());
			e.printStackTrace();
			if (listener != null) {
				listener.onSendMessageFailed(ErrorCode.CAN_NOT_SEND_MESSAGE,
						groupMessageContent);
			}
		}
	}

	public void joinroom(final String roomJID, final String groupName,
			final OnJoinRoomListener listener) {
		KiraGameLog.d("Room joining...");
		if (listener != null) {
			listener.onPreJoinRoom();
		}
		DiscussionHistory history = new DiscussionHistory();
		history.setMaxStanzas(0);
		if (!isLogin()) {
			if (listener != null) {
				listener.onJoinRoomFailed(ErrorCode.CONNECT_SERVER_ERROR,
						roomJID, groupName);
			}
			return;
		}
		QBGroupChatManager qbGroupChatManager = QBChatService.getInstance()
				.getGroupChatManager();
		if (qbGroupChatManager == null) {
			if (listener != null) {
				listener.onJoinRoomFailed(ErrorCode.CONNECT_SERVER_ERROR,
						roomJID, groupName);
			}
			return;
		}

		QBGroupChat qbGroupChat = qbGroupChatManager.getGroupChat(roomJID);
		if (qbGroupChat != null) {
			if (qbGroupChat.isJoined()) {
				if (listener != null) {
					listener.onJoinRoomSuccessed(roomJID, groupName);
				}
				return;
			}

		} else {
			qbGroupChat = qbGroupChatManager.createGroupChat(roomJID);
		}
		final QBGroupChat groupChat = qbGroupChat;

		groupChat.join(history, new QBEntityCallbackImpl() {
			@Override
			public void onSuccess() {
				KiraGameLog.d("join room success");
				groupChat.addMessageListener(groupChatQBMessageListener);
				Handler handler = new Handler(Looper.getMainLooper());
				handler.post(new Runnable() {

					@Override
					public void run() {
						loadMessageFromServer(roomJID, groupName, listener);
					}
				});

			}

			@Override
			public void onError(final List errors) {
				KiraGameLog.d("join room error " + errors);
				if (((String) errors.get(0)).contains(NOT_GROUP_MEMBER)) {
					if (listener != null) {
						listener.onJoinRoomFailed(
								ErrorCode.JOIN_ROOM_FAIL_NOT_MEMBER, roomJID,
								groupName);
					}

				} else {
					if (listener != null) {
						listener.onJoinRoomFailed(ErrorCode.JOIN_ROOM_FAIL,
								roomJID, groupName);
					}
				}

			}
		});
	}

	private void loadMessageFromServer(final String roomJID,
			final String groupName, final OnJoinRoomListener listener) {
		String dialogID = Utils.getDialogIdFromJID(roomJID);
		QBDialog qbDialog = new QBDialog(dialogID);

		QBRequestGetBuilder requestBuilder = new QBRequestGetBuilder();
		requestBuilder.setPagesLimit(100);
		QBChatService.getDialogMessages(qbDialog, requestBuilder,
				new QBEntityCallbackImpl<ArrayList<QBChatMessage>>() {
					@Override
					public void onSuccess(ArrayList<QBChatMessage> messages,
							Bundle args) {
						MessagesDataManager.getInstance(ChatService.this)
								.deleteMessagesByTargetID(roomJID);
						for (QBChatMessage qbChatMessage : messages) {
							// add to database
							String content = qbChatMessage.getBody();
							GroupMessageContent groupMessageContent = GroupMessageContent
									.fromJsonToGroupMessageContent(content);
							if (groupMessageContent != null) {
								MessageContent messageContent = groupMessageContent
										.getMessageContent();

								if (messageContent != null) {
									DetailedMessage detailedMessage = new DetailedMessage();
									HistoryMessage historyMessage = new HistoryMessage();
									detailedMessage.setTargetID(roomJID);
									detailedMessage.setTargetName(groupName);
									detailedMessage
											.setMessageContent(messageContent);
									detailedMessage.setCreateAt(qbChatMessage
											.getDateSent());
									historyMessage.setUnreadNum(0);
									historyMessage
											.setDetailedMessage(detailedMessage);
									historyMessage
											.setChatType(ChatType.CHAT_GROUP);
									MessagesDataManager
											.getInstance(ChatService.this)
											.saveMessageDatabase(historyMessage);
								}
							}
						}

						if (listener != null) {
							listener.onJoinRoomSuccessed(roomJID, groupName);
						}
					}

					@Override
					public void onError(List<String> errors) {
						KiraGameLog.d("getDialogMessages error " + errors);
						if (listener != null) {
							listener.onJoinRoomSuccessed(roomJID, groupName);
						}
					}
				});
	}

	QBMessageListener<QBGroupChat> groupChatQBMessageListener = new QBMessageListener<QBGroupChat>() {
		@Override
		public void processMessage(final QBGroupChat groupChat,
				final QBChatMessage chatMessage) {
			String content = chatMessage.getBody();
			KiraGameLog.d("groupChatQBMessageListener " + content);
			GroupMessageContent groupMessageContent = GroupMessageContent
					.fromJsonToGroupMessageContent(content);
			if (groupMessageContent == null) {
				return;
			}
			MessageContent messageContent = groupMessageContent
					.getMessageContent();
			if (messageContent == null) {
				return;
			}
			String fromGroupJID = groupChat.getJid();
			groupMessageContent.setFromGroupJID(fromGroupJID);
			if (messageContent.getMsgType() == MsgType.TEXT
					|| messageContent.getMsgType() == MsgType.UPDATE_GROUP_INFO) {
				TargetChat currentTargetChat = DataManager.getInstance(
						ChatService.this).getCurrentTargetChat();
				// add to database
				DetailedMessage detailedMessage = new DetailedMessage();
				HistoryMessage historyMessage = new HistoryMessage();
				detailedMessage.setTargetID(fromGroupJID);
				detailedMessage.setTargetName(groupMessageContent
						.getFromGroupName());
				detailedMessage.setCreateAt(chatMessage.getDateSent());
				detailedMessage.setMessageContent(messageContent);
				if (currentTargetChat != null) {
					String currentChatID = currentTargetChat.getFromID();
					if (!currentChatID.equals(fromGroupJID)) {
						historyMessage.setUnreadNum(1);
					} else {
						historyMessage.setUnreadNum(0);
					}
				} else {
					historyMessage.setUnreadNum(1);
				}
				historyMessage.setDetailedMessage(detailedMessage);
				historyMessage.setChatType(ChatType.CHAT_GROUP);
				MessagesDataManager.getInstance(ChatService.this)
						.saveMessageDatabase(historyMessage);
			}

			onReceivedMessageGroup(content);

			// if (DataManager.getInstance(ChatService.this).isShowNotify()) {
			// showNotify(messageContent);
			// }

		}

		@Override
		public void processError(final QBGroupChat groupChat,
				QBChatException error, QBChatMessage originMessage) {

		}

		@Override
		public void processMessageDelivered(QBGroupChat groupChat,
				String messageID) {
			// never be called, works only for 1-1 chat
		}

		@Override
		public void processMessageRead(QBGroupChat groupChat, String messageID) {
			// never be called, works only for 1-1 chat
		}

	};

	public void leaveGroup(String roomJID) {
		QBGroupChatManager qbGroupChatManager = QBChatService.getInstance()
				.getGroupChatManager();
		if (qbGroupChatManager == null) {
			return;
		}

		QBGroupChat qbGroupChat = qbGroupChatManager.getGroupChat(roomJID);
		if (qbGroupChat == null) {
			return;
		}
		qbGroupChat.removeMessageListener(groupChatQBMessageListener);
		qbGroupChat.removeParticipantListener(participantListener);
		try {
			qbGroupChat.leave();
			qbGroupChat = null;
		} catch (NotConnectedException | XMPPException e) {
			e.printStackTrace();
		}
		MessagesDataManager.getInstance(this).setGroupNotJoined(roomJID);
	}

	// PROFILE
	public void updateProfile(boolean isOnline, boolean isInvisible) {
		callBackOnPreUpdateProfile();
		if (!isLogin()) {
			setUpServer();
			return;
		}
		User currentUser = DataManager.getInstance(ChatService.this)
				.getCurrentUser();
		UserInfo userInfo = currentUser.getUserInfo();
		currentQBUser.setFullName(currentUser.getName());
		currentQBUser.setOldPassword(PASSWORD_DEFAULT);
		// currentQBUser.setFacebookId(userInfo.getFaceBookID());
		StringifyArrayList<String> oldTags = currentQBUser.getTags();
		StringifyArrayList<String> tags = new StringifyArrayList<String>();
		tags.add(UserInfo.genderText[userInfo.getGender().ordinal()]);

		if (isOnline) {
			tags.add(ONLINE);
		} else {
			tags.add(OFFLINE);
		}
		if (isInvisible) {
			tags.add(INVISIBLE);
		} else {
			tags.add(VISIBLE);
		}

		if (oldTags.contains(BLOCKED_USER)) {
			tags.add(BLOCKED_USER);
		} else {
			tags.add(NORMAL_USER);
		}
		currentQBUser.setTags(tags);
		currentQBUser.setCustomData(userInfo.toJson());

		QBUsers.updateUser(currentQBUser, new QBEntityCallbackImpl<QBUser>() {
			@Override
			public void onSuccess(QBUser qbuser, Bundle args) {
				KiraGameLog.d("UpdateProfile successful");
				callBackOnFinishedUpdateProfile(null);
			}

			@Override
			public void onError(List<String> errors) {
				KiraGameLog.d("UpdateProfile Errors " + errors);
				callBackOnFinishedUpdateProfile("Update Profile error: "
						+ errors.toString());
			}
		});
	}

	public void logOut() {
		try {
			exit();
			if (isLogin()) {
				QBChatService.getInstance().stopAutoSendPresence();
				QBChatService.getInstance().removeConnectionListener(
						connectionListener);
				leaveChat();

				QBChatService.getInstance().logout(new QBEntityCallbackImpl() {

					@Override
					public void onSuccess() {
						// success
						QBChatService.getInstance().destroy();
						callBackOnFinishedLogOut(null);
						stopSelf();
					}

					@Override
					public void onError(final List list) {
						KiraGameLog.d("Log out error: " + list);
						callBackOnFinishedLogOut("Log out error");
						stopSelf();
					}
				});
			} else {
				stopSelf();
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			stopSelf();
		}

	}

	public void exit() {
		try {
			callBackOnPreLogOut();
			boolean isInvisible = DataManager.getInstance(this)
					.getInvisibleSetting();
			updateProfile(false, isInvisible);
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	@Override
	public void onDestroy() {
		KiraGameLog.d("----------- ChatService onDestroy() ------------");
		if (networkReceiver != null) {
			unregisterReceiver(networkReceiver);
		}
		MessagesDataManager.getInstance(this).closeDB();
		super.onDestroy();
	}

	protected void notifyConnectionOffline() {
		updateUI(UI_NAME_MAIN, MAIN_ACTION_NOTIFY_CONNECTION_OFFLINE, null);
		updateUI(UI_NAME_SPLASH, SPLASH_ACTION_NOTIFY_CONNECTION_OFFLINE, null);
	}

	protected void notifyConnectionOnline() {
		updateUI(UI_NAME_MAIN, MAIN_ACTION_NOTIFY_CONNECTION_ONLINE, null);
		updateUI(UI_NAME_SPLASH, SPLASH_ACTION_NOTIFY_CONNECTION_ONLINE, null);
	}

	protected void onReceivedMessageGroup(String json) {
		updateUI(UI_NAME_MAIN, MAIN_ACTION_ON_RECEIVED_MESSAGE_GROUP, json);
	}

	protected void onReceivedMessagePrivate(String json) {
		updateUI(UI_NAME_MAIN, MAIN_ACTION_ON_RECEIVED_MESSAGE_PRIVATE, json);
	}

	private void updateUI(String nameUI, String action, String message) {
		Intent intent = null;
		switch (nameUI) {
		case UI_NAME_MAIN:
			intent = new Intent(BROADCAST_NAME_MAIN);
			break;
		// case UI_NAME_CHAT_PRIVATE:
		// intent = new Intent(BROADCAST_NAME_CHAT_PRIVATE);
		// break;
		case UI_NAME_SPLASH:
			intent = new Intent(BROADCAST_NAME_SPLASH);
			break;
		default:
			return;
		}

		intent.putExtra(CONTROL_ACTION, action);
		if (message != null) {
			intent.putExtra(CONTROL_MESSAGE, message);
		}
		broadcaster.sendBroadcast(intent);
	}

	// MAIN CALLBACK

	private void callBackOnPreSetupServer() {
		isSetupDone = false;
		updateUI(UI_NAME_SPLASH, SPLASH_ACTION_ON_PRE_SETUP_SERVER, null);
		updateUI(UI_NAME_MAIN, MAIN_ACTION_ON_PRE_SETUP_SERVER, null);
	}

	private void callBackOnFinishedSetupServer(String error) {
		isSetupDone = true;
		updateUI(UI_NAME_SPLASH, SPLASH_ACTION_ON_FINISHED_SETUP_SERVER, error);
		updateUI(UI_NAME_MAIN, MAIN_ACTION_ON_FINISHED_SETUP_SERVER, error);
	}

	private void callBackOnPreUpdateProfile() {
		updateUI(UI_NAME_MAIN, MAIN_ACTION_ON_PRE_UPDATE_PROFILE, null);
	}

	private void callBackOnFinishedUpdateProfile(String error) {
		updateUI(UI_NAME_MAIN, MAIN_ACTION_ON_FINISHED_UPDATE_PROFILE, error);
	}

	// private void callBackOnPreJoinRoom() {
	// updateUI(UI_NAME_MAIN, MAIN_ACTION_ON_PRE_JOIN_ROOM, null);
	// }
	//
	// private void callBackOnFinishedJoinRoom(String error) {
	// updateUI(UI_NAME_MAIN, MAIN_ACTION_ON_FINISHED_JOIN_ROOM, error);
	// }

	private void callBackOnUserEnteredRoom(String userID) {
		updateUI(UI_NAME_MAIN, MAIN_ACTION_ON_USER_ENTERED_ROOM, userID);
	}

	private void callBackOnUserLeftRoom(String userID) {
		updateUI(UI_NAME_MAIN, MAIN_ACTION_ON_USER_LEFT_ROOM, userID);
	}

	private void callBackOnPreLogOut() {
		updateUI(UI_NAME_MAIN, MAIN_ACTION_ON_PRE_LOG_OUT, null);
	}

	private void callBackOnFinishedLogOut(String error) {
		updateUI(UI_NAME_MAIN, MAIN_ACTION_ON_FINISHED_LOG_OUT, error);
	}

	private synchronized void showNotify(MessageContent msgContent) {
		if (!DataManager.getInstance(this).getNotificationSetting()) {
			return;
		}
		try {
			String messageContent = "";
			if (msgContent.getMsgType() == MsgType.TEXT) {
				messageContent = msgContent.getContent();
			} else if (msgContent.getMsgType() == MsgType.SEND_CHAT_VIDEO_REQUEST) {
				messageContent = "You sent a video calling request...";
			} else if (msgContent.getMsgType() == MsgType.RECEIVE_CHAT_VIDEO_REQUEST) {
				messageContent = msgContent.getName()
						+ " sent a video calling request...";
			} else if (msgContent.getMsgType() == MsgType.ACCEPT_VIDEO_CALL) {
				messageContent = msgContent.getContent();
			} else if (msgContent.getMsgType() == MsgType.REJECT_VIDEO_CALL) {
				messageContent = msgContent.getContent();
			} else if (msgContent.getMsgType() == MsgType.IMAGE) {
				messageContent = "Send an image!";
			}
			final String msgText = messageContent;
			final long when = System.currentTimeMillis();
			final String title = msgContent.getName();

			Intent notificationIntent = new Intent(this, MainActivity.class);
			notificationIntent.putExtra(Common.ExtraKey.USER_NAME,
					msgContent.getName());
			notificationIntent.putExtra(Common.ExtraKey.USER_ID,
					Integer.toString(msgContent.getFromUserID()));
			// DataManager.getInstance(this).setNotifyUserId(
			// String.valueOf(msgContent.getFromUserID()));
			// DataManager.getInstance(this).setNotifyUserName(
			// msgContent.getName());
			notificationIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
					| Intent.FLAG_ACTIVITY_SINGLE_TOP);
			// notificationIntent.setAction(Intent.ACTION_MAIN);
			// notificationIntent.addCategory(Intent.CATEGORY_LAUNCHER);
			int iUniqueId = (int) (System.currentTimeMillis());
			final PendingIntent intent = PendingIntent.getActivity(this,
					iUniqueId, notificationIntent,
					PendingIntent.FLAG_UPDATE_CURRENT);

			final int id = msgContent.getFromUserID().hashCode();

			if (msgContent.getAvatarID() != null) {
				AQuery aq = new AQuery(this);
				String url = Utils.getSmallAvatarFacebook(msgContent
						.getAvatarID());
				aq.id(new ImageView(this)).image(url, true, true, 100, 0,
						new BitmapAjaxCallback() {
							@Override
							public void callback(String url, ImageView iv,
									Bitmap bm, AjaxStatus status) {
								if (bm != null) {
									showNotification(msgText, bm, title, when,
											intent, id);
								} else {
									showNotification(msgText, null, title,
											when, intent, id);
								}
							}
						});
			} else {
				showNotification(messageContent, null, title, when, intent, id);
			}

		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	public synchronized void showNotification(String message, Bitmap icon,
			String title, long when, PendingIntent intent, int id) {
		KiraGameLog.d(String.format("show notication message: %s, title: %s",
				message, title));
		builder.setContentText(message);
		builder.setSmallIcon(R.drawable.ic_launcher);
		builder.setLights(0xFFFF8800, 200, 200);
		builder.setWhen(when);
		if (icon != null) {
			builder.setLargeIcon(icon);
		} else {
			builder.setLargeIcon(stub);
		}
		if (title != null) {
			builder.setContentTitle(title);
		}
		if (intent != null) {
			builder.setContentIntent(intent);
		}
		if (DataManager.getInstance(this).getVibrationSetting()) {
			long[] pattern = { 0, 100 };
			builder.setVibrate(pattern);
		}
		if (DataManager.getInstance(this).getSoundSetting()) {
			builder.setSound(RingtoneManager
					.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));
		} else {
			builder.setSound(null);
		}
		builder.setAutoCancel(true);
		Notification notification = builder.build();
		notificationManager.notify(id, notification);
	}

	public boolean isLogin() {

		if (!QBChatService.isInitialized()) {
			return false;
		}
		try {
			String token = BaseService.getBaseService().getToken();
			if (token == null) {
				KiraGameLog.d("lost session!");
				return false;
			}
		} catch (Exception e) {
			KiraGameLog.d("lost session!");
			e.printStackTrace();
			return false;
		}

		try {
			long timeExpiration = BaseService.getBaseService()
					.getTokenExpirationDate().getTime();
			long now = new Date().getTime();

			// check session over (120-30) min, recreate session
			if ((timeExpiration - now) < 1000 * 60 * 30) {
				KiraGameLog.d("Session Expiration ");
				return false;
			}

		} catch (Exception e) {
			KiraGameLog.d("Session Expiration ");
			e.printStackTrace();
			return false;
		}

		return QBChatService.getInstance().isLoggedIn();
	}

	private void rejoinAllJoinedGroup() {

		List<GroupChat> groupChats = MessagesDataManager.getInstance(this)
				.getAllJoinedGroup();
		for (GroupChat groupChat : groupChats) {
			KiraGameLog.d("rejoinAllJoinedGroup " + groupChat.getGroupJID());
			leaveGroup(groupChat.getGroupJID());
			ChatService.getInstance().joinroom(groupChat.getGroupJID(),
					groupChat.getName(), null);
		}
	}

}
